package com.xiaowu.friend.async;


import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.xiaowu.common.core.constants.CacheConstants;
import com.xiaowu.common.core.constants.CacheLockConstants;
import com.xiaowu.common.core.constants.Constants;
import com.xiaowu.common.core.enums.ExamListType;
import com.xiaowu.common.redis.service.RedisService;
import com.xiaowu.common.redis.service.RedissonLockService;
import com.xiaowu.friend.domain.exam.Exam;
import com.xiaowu.friend.domain.exam.ExamQuestion;
import com.xiaowu.friend.domain.exam.vo.ExamRankVO;
import com.xiaowu.friend.domain.exam.vo.ExamVO;
import com.xiaowu.friend.mapper.exam.ExamMapper;
import com.xiaowu.friend.mapper.exam.ExamQuestionMapper;
import com.xiaowu.friend.mapper.user.UserExamMapper;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;


/**
 * 异步刷新竞赛相关缓存功能类
 */
@Slf4j
@Component
public class ExamCacheAsync {

    @Autowired
    private ExamMapper examMapper;

    @Autowired
    private UserExamMapper userExamMapper;

    @Autowired
    private ExamQuestionMapper examQuestionMapper;

    @Autowired
    private RedisService redisService;

    @Autowired
    private RedissonLockService redissonLockService;


    //刷新竞赛列表缓存
    @Async(value = "threadPoolTaskExecutor")
    public void refreshCache(Integer examListType, Long userId) {
        // 获取锁
        RLock rLock = redissonLockService.acquire(CacheLockConstants.EXAM_REFRESH_LOCK, -1);
        // 获取失败，跳过执行
        if (null == rLock) {
            return;
        }

        try {
            List<Exam> examList = new ArrayList<>();
            if (ExamListType.EXAM_UN_FINISH_LIST.getValue().equals(examListType)) {
                //查询未完赛的竞赛列表
                examList = examMapper.selectList(new LambdaQueryWrapper<Exam>()
                        .select(Exam::getExamId, Exam::getTitle, Exam::getStartTime, Exam::getEndTime)
                        .gt(Exam::getEndTime, LocalDateTime.now())
                        .eq(Exam::getStatus, Constants.TRUE)
                        .orderByDesc(Exam::getCreateTime));
            } else if (ExamListType.EXAM_HISTORY_LIST.getValue().equals(examListType)) {
                //查询历史竞赛
                examList = examMapper.selectList(new LambdaQueryWrapper<Exam>()
                        .select(Exam::getExamId, Exam::getTitle, Exam::getStartTime, Exam::getEndTime)
                        .le(Exam::getEndTime, LocalDateTime.now())
                        .eq(Exam::getStatus, Constants.TRUE)
                        .orderByDesc(Exam::getCreateTime));
            }else if (ExamListType.USER_EXAM_LIST.getValue().equals(examListType)) {
                //查询用户参加过的竞赛
                List<ExamVO> examVOList = userExamMapper.selectUserExamList(userId);
                examList = BeanUtil.copyToList(examVOList, Exam.class);
            }
            if (CollectionUtil.isEmpty(examList)) {
                return;
            }

            Map<String, Exam> examMap = new HashMap<>();
            List<Long> examIdList = new ArrayList<>();
            for (Exam exam : examList) {
                examMap.put(getDetailKey(exam.getExamId()), exam);
                examIdList.add(exam.getExamId());
            }
            redisService.multiSet(examMap);  //刷新详情缓存
            redisService.deleteObject(getExamListKey(examListType, userId));
            redisService.rightPushAll(getExamListKey(examListType, userId), examIdList);      //刷新列表缓存
        } catch (Exception e) {
            log.error("刷新竞赛相关缓存失败");
        } finally {
            // 释放锁
            if (rLock.isHeldByCurrentThread() && rLock.isLocked()) {
                redissonLockService.releaseLock(rLock);
            }
        }
    }

    /**
     * 刷新竞赛题目缓存
     * @param examId 竞赛ID
     */
    @Async(value = "threadPoolTaskExecutor")
    public void refreshExamQuestionCache(Long examId) {
        // 获取锁
        RLock rLock = redissonLockService.acquire(CacheLockConstants.EX_QUESTION_REFRESH_LOCK, -1);
        // 获取失败跳过执行
        if (null == rLock) {
            return;
        }

        try {
            // 根据examId查询ExamQuestion表中的数据，并按照questionOrder降序排列
            List<ExamQuestion> examQuestionList = examQuestionMapper
                    .selectList(new LambdaQueryWrapper<ExamQuestion>()
                            .select(ExamQuestion::getQuestionId).eq(ExamQuestion::getExamId, examId)
                            .orderByDesc(ExamQuestion::getQuestionOrder));
            // 如果查询结果为空，则直接返回
            if (CollectionUtil.isEmpty(examQuestionList)) {
                return;
            }
            // 将查询结果中的questionId提取出来，存入一个List中
            List<Long> questionIdList = examQuestionList.stream()
                    .map(ExamQuestion::getQuestionId).toList();
            // 将questionIdList存入Redis中
            redisService.rightPushAll(getExamQuestionListKey(examId), questionIdList);
            // 计算当前时间到第二天零点的时间差，以秒为单位
            long seconds = ChronoUnit.SECONDS.between(LocalDateTime.now(),
                    LocalDateTime.now().plusDays(1).withHour(0)
                            .withMinute(0).withSecond(0).withNano(0));
            // 设置Redis中key的过期时间
            redisService.expire(getExamQuestionListKey(examId), seconds, TimeUnit.SECONDS);
        } catch (Exception e) {
            log.error("刷新竞赛题目缓存失败！");
        } finally {
            // 释放锁
            if (rLock.isHeldByCurrentThread() && rLock.isLocked()) {
                redissonLockService.releaseLock(rLock);
            }
        }
    }


    //获取竞赛列表key
    private String getExamListKey(Integer examListType, Long userId) {
        if (ExamListType.EXAM_UN_FINISH_LIST.getValue().equals(examListType)) {
            return CacheConstants.EXAM_UNFINISHED_LIST;
        } else if (ExamListType.EXAM_HISTORY_LIST.getValue().equals(examListType)) {
            return CacheConstants.EXAM_HISTORY_LIST;
        } else {
            return CacheConstants.USER_EXAM_LIST + userId;
        }
    }


    //获取竞赛详情key
    private String getDetailKey(Long examId) {
        return CacheConstants.EXAM_DETAIL + examId;
    }


    //获取竞赛题目列表key
    private String getExamQuestionListKey(Long examId) {
        return CacheConstants.EXAM_QUESTION_LIST + examId;
    }
}
